#ifndef __M__QUEUE_H_
#define __M__QUEUE_H_

#include "../mqCommon/Helper.hpp"
#include "../mqCommon/Logger.hpp"
#include "../mqCommon/proto/mq_message.pb.h"
#include <iostream>
#include <unordered_map>
#include <memory>
#include <mutex>

using namespace mq_message;
using namespace mq;
using namespace std;

namespace mq
{
    struct MsgQueue;
    using q_ptr = shared_ptr<MsgQueue>;
    using MsgQueueMap = unordered_map<string, q_ptr>;
    using Map = google::protobuf::Map<string, string>;

    // 1.队列类
    struct MsgQueue
    {
        string _name;      // 队列名称
        bool _exclusive;   // 是否独占标志
        bool _durable;     // 持久化标志
        bool _auto_delete; // 自动删除标志
        Map _args;         // 其它参数

        MsgQueue(const string &name, bool exclusive,
                 bool durable, bool auto_delete,
                 Map &args)
            : _name(name), _exclusive(exclusive), _durable(durable), _auto_delete(auto_delete), _args(args) {}
        MsgQueue() {}

        // 数据库存储以格式化字符串key=val&key=val...形式存储
        void setArgs(const string &str_args) // 传入格式化字符串k=v&k=v...，将其中键值对提取到args中
        {
            vector<string> args_list;
            StrHelper::split(str_args, "&", args_list); // key=val
            for (auto &str : args_list)
            {
                size_t pos = str.find("=");
                string key = str.substr(0, pos);
                string val = str.substr(pos + 1);
                _args[key] = val;
            }
        }
        string getArgs() // 将args中键值对提取，格式化为字符串k=v&k=v...
        {
            string ret;
            auto it = _args.begin();
            while (it != _args.end())
            {
                ret += it->first + "=" + it->second + "&";
                it++;
            }
            if (!ret.empty())
                ret.pop_back(); // 去掉最后一个&
            return ret;
        }
    };

    // 2.队列数据持久化管理类--数据存储在sqlite中
    class MsgQueueMapper
    {
    public:
        MsgQueueMapper(const string &dbfile) : _sql_helper(dbfile)
        {
            // 创建文件前先创建目标路径
            string path = FileHelper::parentDirectory(dbfile);
            bool ret1 = FileHelper::createDirectory(path);
            if (!ret1)
            {
                ELOG("创建数据库文件路径时出错：%s", path);
                abort();
            }

            bool ret2 = _sql_helper.open(); // 打开/创建数据库文件
            if (!ret2)
            {
                ELOG("打开数据库文件失败：%s", dbfile);
                abort();
            }
            createTable();
        }
        void createTable() // 创建队列数据表
        {
#define CREATE_TABLE "create table if not exists queue_table(\
                                name varchar(32) primary key, \
                                exclusive int, durable int, auto_delete int, \
                                args varchar(128));"
            bool ret = _sql_helper.exec(CREATE_TABLE, NULL, NULL);
            if (!ret)
            {
                ELOG("创建队列数据库表失败");
                abort();
            }
        }
        void removeTable() // 删除队列数据表
        {
#define DROP_TABLE "drop table if exists queue_table;"
            bool ret = _sql_helper.exec(DROP_TABLE, NULL, NULL);
            if (!ret)
            {
                ELOG("删除队列数据库表失败");
                abort();
            }
        }
        bool insert(q_ptr &MsgQueue) // 新增队列
        {
#define INSERT_SQL "insert into queue_table values('%s',%d,%d,%d,'%s');"
            char sql_str[4096];
            string str_args = MsgQueue->getArgs();
            snprintf(sql_str, sizeof(sql_str) - 1, INSERT_SQL, MsgQueue->_name.c_str(), MsgQueue->_exclusive, MsgQueue->_durable, MsgQueue->_auto_delete, str_args.c_str());
            bool ret = _sql_helper.exec(sql_str, NULL, NULL);
            if (!ret)
            {
                ELOG("新增队列失败：%s", MsgQueue->_name.c_str());
                return false;
            }
            return true;
        }
        bool remove(const string &name) // 删除队列
        {
            std::stringstream ss;
            ss << "delete from queue_table where name='" << name << "';";
            bool ret = _sql_helper.exec(ss.str(), NULL, NULL);
            if (!ret)
            {
                ELOG("删除队列失败：%s", name.c_str());
                return false;
            }
            return true;
        }
        // q_ptr getOne(const string &name);      // 获取一台队列
        MsgQueueMap recovery() // 获取队列数据库表中所有队列：恢复数据
        {
            string select_sql = "select name, exclusive, durable, auto_delete, args from queue_table;"; // 查询所有队列信息
            MsgQueueMap _MsgQueues;
            bool ret = _sql_helper.exec(select_sql, selectCallback, (void *)&_MsgQueues);
            if (!ret)
            {
                ELOG("恢复队列数据过程出错");
            }
            return _MsgQueues;
        }

    private:
        static int selectCallback(void *args, int col_num, char **row, char **fields) // 查询回调，每一行都调用该回调
        {
            MsgQueueMap *result = (MsgQueueMap *)args;
            auto queue = make_shared<MsgQueue>(); // 创建一个队列对象
            queue->_name = row[0];
            queue->_exclusive = (bool)stoi(row[1]);
            queue->_durable = (bool)stoi(row[2]);
            queue->_auto_delete = (bool)stoi(row[3]);
            if (row[4])
                queue->setArgs(row[4]);
            result->insert(make_pair(queue->_name, queue));
            return 0;
        }

    private:
        SqliteHelper _sql_helper;
    };

    // 3.队列数据内存管理类
    class MsgQueueManager
    {
    public:
        using manager_ptr = shared_ptr<MsgQueueManager>;
        MsgQueueManager(const string &dbfile) : _mapper(dbfile)
        {
            _MsgQueues = _mapper.recovery(); // 恢复历史数据
        }
        bool declareMsgQueue(const string &name, bool exclusive,
                             bool durable, bool auto_delete,
                             Map &args) // 声明队列
        {
            unique_lock<mutex> lock(mtx); // 多线程下
            if (exists(name))
                return true;                                                                 // 若已存在，不需要重复添加
            auto queue = make_shared<MsgQueue>(name, exclusive, durable, auto_delete, args); // 创建一个队列对象
            if (queue->_durable)
            {
                bool ret = _mapper.insert(queue);
                if (!ret)
                {
                    DLOG("队列持久化失败：%s", name.c_str());
                    return false;
                }
            } // 该队列需要持久化，才加入mapper中
            _MsgQueues.insert(make_pair(queue->_name, queue));
            return true; // 若已存在，不需要重复添加
        }
        void deleteMsgQueue(const string &name) // 删除队列
        {
            unique_lock<mutex> lock(mtx); // 多线程下
            if (!exists(name))            // 不存在，直接返回
                return;
            q_ptr queue = _MsgQueues[name];
            if (queue->_durable)
                _mapper.remove(name);
            _MsgQueues.erase(name);
        }
        q_ptr selectMsgQueue(const string &name) // 获取指定队列
        {
            unique_lock<mutex> lock(mtx); // 多线程下
            if (!exists(name))            // 不存在，直接返回空
                return q_ptr();
            return _MsgQueues[name];
        }
        bool exists(const string &name) // 判断队列是否存在:需要加锁
        {
            auto it = _MsgQueues.find(name);
            return it != _MsgQueues.end();
        }
        void clear() // 销毁所有数据
        {
            unique_lock<mutex> lock(mtx);
            _mapper.removeTable();
            _MsgQueues.clear();
        }
        size_t size() // 返回队列数量
        {
            unique_lock<mutex> lock(mtx);
            return _MsgQueues.size();
        }
        MsgQueueMap allQueue() // 返回所有队列数据
        {
            unique_lock<mutex> lock(mtx);
            return _MsgQueues;
        }

    private:
        mutex mtx;              // 加锁保护
        MsgQueueMapper _mapper; // 持久化管理类
        MsgQueueMap _MsgQueues; // 队列map
    };

}

#endif